﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace SnakeX
{
	/// <summary>
	/// This class represents a snake.
	/// </summary>
	public class SnakeXSnake
	{
		/// <summary>
		/// Constructor.
		/// Initializing members.
		/// </summary>
		/// <param name="direction">Snake movement direction</param>
		/// <param name="side">Snake side length</param>
		/// <param name="linksCount">Number of links to start with</param>
		/// <param name="screenWidth">Width of the screen</param>
		/// <param name="screenHeight">Height of the screen</param>
		public SnakeXSnake(MovementDirections direction, int side,
			int linksCount, int screenWidth, int screenHeight)
		{
			this.direction = direction;
			this.side = side;
			this.screenWidth = screenWidth;
			this.screenHeight = screenHeight;

			// Adding initial snake links.
			snakeLinks = new List<Point>();

			for (int i = (((screenWidth / (2 * side)) * side) - (side * 4));
				i <= (((screenWidth / (2 * side)) * side) + (side * 4)); i += side)
			{
				snakeLinks.Add(new Point(i, (screenHeight / (2 * side)) * side));
			}

			// Setting initial movement coordinates.
			movementCoordinates = new Point(side, 0);
		}


		/// <summary>
		/// Moving the snake by the movement coordinates.
		/// Can change direction.
		/// </summary>
		public void Move()
		{
			// Moving all the links but the first one forward.
			for (int i = snakeLinks.Count - 1; i > 0; i--)
			{
				snakeLinks[i] = snakeLinks[i - 1];
			}

			// Setting first link new position.
			snakeLinks[0] = new Point(
				snakeLinks[0].X - movementCoordinates.X,
				snakeLinks[0].Y - movementCoordinates.Y);
		}




		#region Links Methods


		/// <summary>
		/// Adding a link to the snake in the given position.
		/// </summary>
		/// <param name="position">Position of the link to add</param>
		public void AddLink(Point position)
		{
			snakeLinks.Add(position);
		}

		/// <summary>
		/// Getting the link in the given index in the links list.
		/// </summary>
		/// <param name="index">Index of the link to get</param>
		/// <returns>Link in the given index</returns>
		public Point GetLink(int index)
		{
			return snakeLinks[index];
		}

		/// <summary>
		/// Setting link in the given index to the given new position.
		/// </summary>
		/// <param name="index">Index of the link to set</param>
		/// <param name="newPosition">New position to set</param>
		public void SetLink(int index, Point newPosition)
		{
			snakeLinks[index] = newPosition;
		}

		/// <summary>
		/// Setting link in the given old position to the given new position.
		/// </summary>
		/// <param name="oldPosition">Old position of the link to set</param>
		/// <param name="newPosition">New position to set</param>
		public void SetLink(Point oldPosition, Point newPosition)
		{
			snakeLinks[snakeLinks.IndexOf(oldPosition)] = newPosition;
		}


		#endregion


		#region Collision Methods


		/// <summary>
		/// Looking for self collisions in the snake.
		/// </summary>
		/// <returns>True if there is a self collision. False otherwise</returns>
		public bool SelfCollision()
		{
			for (int i = 1; i < snakeLinks.Count; i++)
			{
				// Checking if there is a collision between the head and this link.
				if ((snakeLinks[0].X == snakeLinks[i].X) && 
					(snakeLinks[0].Y == snakeLinks[i].Y))
				{
					// Collision.
					return true;
				}
			}

			return false;
		}

		/// <summary>
		/// Looking for collision with the apple in the given position.
		/// If there is a collision - enlarging snake.
		/// </summary>
		/// <param name="applePos">
		/// Position of the apple to check collision with.
		/// </param>
		/// <returns>True if there is a collision. False otherwise</returns>
		public bool AppleCollision(Point applePos)
		{
			if ((snakeLinks[0].X == applePos.X) &&
				(snakeLinks[0].Y == applePos.Y))
			{
				// Collision. Enlarging snake.
				Point lastLink = snakeLinks[snakeLinks.Count - 1];
				Move();
				AddLink(lastLink);

				return true;
			}
			else
			{
				return false;
			}
		}

		/// <summary>
		/// Lokking for snake collision with the screen border.
		/// </summary>
		/// <returns>True if there is a collision. False otherwise</returns>
		public bool BorderCollision()
		{
			if ((snakeLinks[0].X < 0) ||
				(snakeLinks[0].X + side > screenWidth) ||
				(snakeLinks[0].Y < 0) ||
				(snakeLinks[0].Y + side > screenHeight))
			{
				// Collision.
				return true;
			}
			else
			{
				return false;
			}
		}


		/// <summary>
		/// Checking if the given position is inside the snake.
		/// </summary>
		/// <param name="position">Position to check</param>
		/// <returns>
		/// True if the position is inside the snake. False otherwise.
		/// </returns>
		public bool SnakeCollision(Point position)
		{
			foreach (Point link in snakeLinks)
			{
				if ((link.X == position.X) && (link.Y == position.Y))
				{
					// Position is inside the snake.
					return true;
				}
			}

			// Position is not inside the snake.
			return false;
		}


		#endregion


		/// <summary>
		/// Returns snake links count.
		/// </summary>
		public int LinksCount
		{
			get { return snakeLinks.Count; }
		}



		# region Members


		/// <summary>
		/// Snake side length.
		/// </summary>
		private int side;

		public int Side
		{
			get { return side; }
			set { side = value; }
		}

		/// <summary>
		/// Snake movement coordinates.
		/// </summary>
		private Point movementCoordinates;

		public Point MovementCoordinates
		{
			get { return movementCoordinates; }
			set { movementCoordinates = value; }
		}


		/// <summary>
		/// Current snake movement direction.
		/// </summary>
		private MovementDirections direction;

		public MovementDirections Direction
		{
			get { return direction; }
			set { direction = value; }
		}



		/// <summary>
		/// List of available movement directions.
		/// </summary>
		public enum MovementDirections {Left, Right, Up, Down};




		/// <summary>
		/// Snake links list object.
		/// </summary>
		private List<Point> snakeLinks;


		/// <summary>
		/// Screen width.
		/// </summary>
		private int screenWidth;

		/// <summary>
		/// Screen height.
		/// </summary>
		private int screenHeight;


		#endregion
	}
}
